/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// Bit Vector data structure
// Implementation

#include "include/BitVector.hh"

BitVector::BitVector(unsigned char *baseBytePtr,
                     unsigned baseBitOffset,
                     unsigned totNumBits) {
    setup(baseBytePtr, baseBitOffset, totNumBits);
}

void BitVector::setup(unsigned char *baseBytePtr,
                      unsigned baseBitOffset,
                      unsigned totNumBits) {
    fBaseBytePtr = baseBytePtr;
    fBaseBitOffset = baseBitOffset;
    fTotNumBits = totNumBits;
    fCurBitIndex = 0;
}

static unsigned char const singleBitMask[8]
        = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};

#define MAX_LENGTH 32

void BitVector::putBits(unsigned from, unsigned numBits) {
    if (numBits == 0) return;

    unsigned char tmpBuf[4];
    unsigned overflowingBits = 0;

    if (numBits > MAX_LENGTH) {
        numBits = MAX_LENGTH;
    }

    if (numBits > fTotNumBits - fCurBitIndex) {
        overflowingBits = numBits - (fTotNumBits - fCurBitIndex);
    }

    tmpBuf[0] = (unsigned char) (from >> 24);
    tmpBuf[1] = (unsigned char) (from >> 16);
    tmpBuf[2] = (unsigned char) (from >> 8);
    tmpBuf[3] = (unsigned char) from;

    shiftBits(fBaseBytePtr, fBaseBitOffset + fCurBitIndex, /* to */
              tmpBuf, MAX_LENGTH - numBits, /* from */
              numBits - overflowingBits /* num bits */);
    fCurBitIndex += numBits - overflowingBits;
}

void BitVector::put1Bit(unsigned bit) {
    // The following is equivalent to "putBits(..., 1)", except faster:
    if (fCurBitIndex >= fTotNumBits) { /* overflow */
        return;
    } else {
        unsigned totBitOffset = fBaseBitOffset + fCurBitIndex++;
        unsigned char mask = singleBitMask[totBitOffset % 8];
        if (bit) {
            fBaseBytePtr[totBitOffset / 8] |= mask;
        } else {
            fBaseBytePtr[totBitOffset / 8] &= ~mask;
        }
    }
}

unsigned BitVector::getBits(unsigned numBits) {
    if (numBits == 0) return 0;

    unsigned char tmpBuf[4];
    unsigned overflowingBits = 0;

    if (numBits > MAX_LENGTH) {
        numBits = MAX_LENGTH;
    }

    if (numBits > fTotNumBits - fCurBitIndex) {
        overflowingBits = numBits - (fTotNumBits - fCurBitIndex);
    }

    shiftBits(tmpBuf, 0, /* to */
              fBaseBytePtr, fBaseBitOffset + fCurBitIndex, /* from */
              numBits - overflowingBits /* num bits */);
    fCurBitIndex += numBits - overflowingBits;

    unsigned result
            = (tmpBuf[0] << 24) | (tmpBuf[1] << 16) | (tmpBuf[2] << 8) | tmpBuf[3];
    result >>= (MAX_LENGTH - numBits); // move into low-order part of word
    result &= (0xFFFFFFFF << overflowingBits); // so any overflow bits are 0
    return result;
}

unsigned BitVector::get1Bit() {
    // The following is equivalent to "getBits(1)", except faster:

    if (fCurBitIndex >= fTotNumBits) { /* overflow */
        return 0;
    } else {
        unsigned totBitOffset = fBaseBitOffset + fCurBitIndex++;
        unsigned char curFromByte = fBaseBytePtr[totBitOffset / 8];
        unsigned result = (curFromByte >> (7 - (totBitOffset % 8))) & 0x01;
        return result;
    }
}

void BitVector::skipBits(unsigned numBits) {
    if (numBits > fTotNumBits - fCurBitIndex) { /* overflow */
        fCurBitIndex = fTotNumBits;
    } else {
        fCurBitIndex += numBits;
    }
}

unsigned BitVector::get_expGolomb() {
    unsigned numLeadingZeroBits = 0;
    unsigned codeStart = 1;

    while (get1Bit() == 0 && fCurBitIndex < fTotNumBits) {
        ++numLeadingZeroBits;
        codeStart *= 2;
    }

    return codeStart - 1 + getBits(numLeadingZeroBits);
}

int BitVector::get_expGolombSigned() {
    unsigned codeNum = get_expGolomb();

    if ((codeNum & 1) == 0) { // even
        return -(int) (codeNum / 2);
    } else { // odd
        return (codeNum + 1) / 2;
    }
}

void shiftBits(unsigned char *toBasePtr, unsigned toBitOffset,
               unsigned char const *fromBasePtr, unsigned fromBitOffset,
               unsigned numBits) {
    if (numBits == 0) return;

    /* Note that from and to may overlap, if from>to */
    unsigned char const *fromBytePtr = fromBasePtr + fromBitOffset / 8;
    unsigned fromBitRem = fromBitOffset % 8;
    unsigned char *toBytePtr = toBasePtr + toBitOffset / 8;
    unsigned toBitRem = toBitOffset % 8;

    while (numBits-- > 0) {
        unsigned char fromBitMask = singleBitMask[fromBitRem];
        unsigned char fromBit = (*fromBytePtr) & fromBitMask;
        unsigned char toBitMask = singleBitMask[toBitRem];

        if (fromBit != 0) {
            *toBytePtr |= toBitMask;
        } else {
            *toBytePtr &= ~toBitMask;
        }

        if (++fromBitRem == 8) {
            ++fromBytePtr;
            fromBitRem = 0;
        }
        if (++toBitRem == 8) {
            ++toBytePtr;
            toBitRem = 0;
        }
    }
}
